package xmla

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

//RequestMonth 请求类别
type RequestMonth int

//request请求类型
const (
	DISCOVER RequestMonth = iota + 1
	EXECUTE
)

func (t RequestMonth) String() string {
	switch t {
	case DISCOVER:
		return "DISCOVER"
	case EXECUTE:
		return "EXECUTE"
	default:
		return "UNKNOWN"
	}
}

//RequestType 请求类型
type RequestType int

const (
	DISCOVER_DATASOURCES RequestType = iota + 1
	DBSCHEMA_CATALOGS
	MDSCHEMA_CUBES
	MDSCHEMA_DIMENSIONS
	MDSCHEMA_HIERARCHIES
	MDSCHEMA_LEVELS
	MDSCHEMA_MEMBERS
	MDSCHEMA_MEASURES
)

func (t RequestType) String() string {
	switch t {
	case DISCOVER_DATASOURCES:
		return "DISCOVER_DATASOURCES"
	case DBSCHEMA_CATALOGS:
		return "DBSCHEMA_CATALOGS"
	case MDSCHEMA_CUBES:
		return "MDSCHEMA_CUBES"
	case MDSCHEMA_DIMENSIONS:
		return "MDSCHEMA_DIMENSIONS"
	case MDSCHEMA_HIERARCHIES:
		return "MDSCHEMA_HIERARCHIES"
	case MDSCHEMA_LEVELS:
		return "MDSCHEMA_LEVELS"
	case MDSCHEMA_MEMBERS:
		return "MDSCHEMA_MEMBERS"
	case MDSCHEMA_MEASURES:
		return "MDSCHEMA_MEASURES"
	default:
		return "UNKNOWN"
	}
}

//DefaultXMLAPort XMLAPort的默认实现
type DefaultXMLAPort struct {
	url      string
	userName string
	password string
}

//SetUrl 设置xmla服务器的url
func (c *DefaultXMLAPort) SetUrl(url string) {
	c.url = url
}

//新建XMLA接口
func NewDefaultXMLAPort(p_url string, p_userName string, p_password string) *DefaultXMLAPort {
	return &DefaultXMLAPort{
		url:      p_url,
		userName: p_userName,
		password: p_password,
	}
}

//SetUserName 设置xmla服务器的访问用户名
func (c *DefaultXMLAPort) SetUserName(userName string) {
	c.userName = userName
}

//SetPassword 设置xmla服务器的访问密码
func (c *DefaultXMLAPort) SetPassword(password string) {
	c.password = password
}

//httpDo 执行xmla请求返回xml字符串
func (c *DefaultXMLAPort) httpDo(xmla string, reqType RequestMonth) ([]byte, error) {
	client := &http.Client{}
	req, err := http.NewRequest("POST", c.url, strings.NewReader(xmla))
	if err != nil {
		return nil, err
	}
	reqTypeStr := reqType.String()
	req.Header.Set("SOAPAction", fmt.Sprintf("\"urn:schemas-microsoft-com:xml-analysis:%s\"", reqTypeStr))
	req.Header.Set("Content-Type", "text/xml; charset=utf-8")
	req.Header.Set("Accept", "application/soap+xml, application/dime, multipart/related, text/*")
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

//discover 执行xmla查询请求返回xml字符串
func (c *DefaultXMLAPort) discover(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties, dType RequestType) ([]RowSet, error) {
	var xmlaBuf bytes.Buffer
	xmlaBuf.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
						 <SOAP-ENV:Envelope
						   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
						   <SOAP-ENV:Body>
           				     <Discover xmlns="urn:schemas-microsoft-com:xml-analysis"
           				               SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">`)

	xmlaBuf.WriteString(fmt.Sprintf("    <RequestType>%s</RequestType>", dType.String()))
	xmlaBuf.WriteString(r.getPropertyListXML("    "))
	xmlaBuf.WriteString(p.getPropertyListXML("    "))
	xmlaBuf.WriteString("    </Discover>")
	xmlaBuf.WriteString("  </SOAP-ENV:Body>")
	xmlaBuf.WriteString("</SOAP-ENV:Envelope>")
	if xmlBytes, err := c.httpDo(xmlaBuf.String(), DISCOVER); err == nil {
		xmlIo := bytes.NewBuffer(xmlBytes)
		return NewRowSets(xml.NewDecoder(xmlIo))
	} else {
		return nil, err
	}
}

//DiscoverDataSources 执行xmla查询DISCOVER_DATASOURCES返回xml字符串
func (c *DefaultXMLAPort) DiscoverDataSources(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {

	return c.discover(r, p, DISCOVER_DATASOURCES)
}

//GetCatalogList 获取目录列表
func (c *DefaultXMLAPort) GetCatalogList(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {
	return c.discover(r, p, DBSCHEMA_CATALOGS)
}

//GetCubeList 获取Cube列表
func (c *DefaultXMLAPort) GetCubeList(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {
	return c.discover(r, p, MDSCHEMA_CUBES)
}

//GetDimensionList 获取维度列表
func (c *DefaultXMLAPort) GetDimensionList(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {
	return c.discover(r, p, MDSCHEMA_DIMENSIONS)
}

//GetHierarchyList 获取层次列表
func (c *DefaultXMLAPort) GetHierarchyList(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {
	return c.discover(r, p, MDSCHEMA_HIERARCHIES)
}

//GetLevelList 获取级别列表
func (c *DefaultXMLAPort) GetLevelList(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {
	return c.discover(r, p, MDSCHEMA_LEVELS)
}

//GetMemberList 获取成员列表
func (c *DefaultXMLAPort) GetMemberList(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {
	return c.discover(r, p, MDSCHEMA_MEMBERS)
}

//GetMeasureList 获取度量值列表
func (c *DefaultXMLAPort) GetMeasureList(r *XMLADiscoverRestrictions, p *XMLADiscoverProperties) ([]RowSet, error) {
	return c.discover(r, p, MDSCHEMA_MEASURES)
}

//Execute 执行MDX语句
func (c *DefaultXMLAPort) Execute(mdx *string, p *XMLAExecuteProperties) (*CubeData, error) {
	mdxBuf := new(bytes.Buffer)
	xml.EscapeText(mdxBuf, []byte(*mdx))
	var xmlaBuf bytes.Buffer
	xmlaBuf.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
						 <SOAP-ENV:Envelope
						   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
						    <SOAP-ENV:Body>
           				       <Execute xmlns="urn:schemas-microsoft-com:xml-analysis"
           				                 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">`)
	xmlaBuf.WriteString(fmt.Sprintf(`<Command>
                                 <Statement>
                                    %s
                                 </Statement>
                              </Command>`, mdxBuf.String()))
	xmlaBuf.WriteString(p.getPropertyListXML("   "))
	xmlaBuf.WriteString("      </Execute>")
	xmlaBuf.WriteString("    </SOAP-ENV:Body>")
	xmlaBuf.WriteString("  </SOAP-ENV:Envelope>")
	if cData, found := getCache(mdxBuf.String()); found {
		return cData, nil
	}
	if xmlBytes, err := c.httpDo(xmlaBuf.String(), EXECUTE); err == nil {
		xmlIo := bytes.NewBuffer(xmlBytes)
		cData, err := NewCubeData(xml.NewDecoder(xmlIo))
		if err != nil {
			return nil, err
		}
		addCache(mdxBuf.String(), cData)
		return cData, nil
	} else {
		return nil, err
	}
}
